package ru.ifmo.network.skype.audio;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.Arrays;

/**
 * @author avhaliullin
 */
public class SimpleFilter implements AudioFilter {
    private static final int rangeTop = 256;
    private static final int rangeBottom = 0;
    private final double[] im;

    public SimpleFilter(Config config) {
        this.im = new double[config.getBufferSize() / 2];
    }

    public static void fourier(int sign, int n,
                               double ar[], double ai[]) {
        double scale = (double) Math.sqrt(1.0f / n);

        int i, j;
        for (i = j = 0; i < n; ++i) {
            if (j >= i) {
                double tempr = ar[j] * scale;
                double tempi = ai[j] * scale;
                ar[j] = ar[i] * scale;
                ai[j] = ai[i] * scale;
                ar[i] = tempr;
                ai[i] = tempi;
            }
            int m = n / 2;
            while (m >= 1 && j >= m) {
                j -= m;
                m /= 2;
            }
            j += m;
        }

        int mmax, istep;
        for (mmax = 1, istep = 2 * mmax; mmax < n; mmax = istep, istep = 2 * mmax) {
            double delta = (double) sign * 3.141592654f / (double) mmax;
            for (int m = 0; m < mmax; ++m) {
                double w = (double) m * delta;
                double wr = (double) Math.cos(w);
                double wi = (double) Math.sin(w);
                for (i = m; i < n; i += istep) {
                    j = i + mmax;
                    double tr = wr * ar[j] - wi * ai[j];
                    double ti = wr * ai[j] + wi * ar[j];
                    ar[j] = ar[i] - tr;
                    ai[j] = ai[i] - ti;
                    ar[i] += tr;
                    ai[i] += ti;
                }
            }
            mmax = istep;
        }
    }

    public byte[] filter(byte[] data) {
        Arrays.fill(im, 0, im.length, 0);
        int n = data.length / 2;
        ShortBuffer shortBuffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
        double[] doubleData = new double[n];
        for (int i = 0; i < n; i++) {
            doubleData[i] = shortBuffer.get(i);
        }
        fourier(1, n, doubleData, im);
        Arrays.fill(doubleData, rangeTop, 256, 0);
        Arrays.fill(im, rangeTop, 256, 0);
        Arrays.fill(doubleData, 0, rangeBottom, 0);
        Arrays.fill(im, 0, rangeBottom, 0);
        fourier(-1, n, doubleData, im);
        for (int i = 0; i < n; i++) {
            shortBuffer.put(i, (short) doubleData[i]);
        }

        return data;
    }
}
